package com.hengpeng.msls.tools;

import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

public class TimeOutMap<K, V>
  implements Map<K, V>
{
  private long taskPeriod = 1L;
  private long timeOut = -1L;
  private Map<K, V> map = null;
  private Map<K, Long> enterTimeMap = null;
  private List<K> keyList = null;
  private Timer timer = null;
  
  public TimeOutMap(long timeOut)
  {
    this.timeOut = timeOut;
    this.map = Collections.synchronizedMap(new HashMap());
    this.enterTimeMap = Collections.synchronizedMap(new HashMap());
    this.keyList = Collections.synchronizedList(new LinkedList());
    if (timeOut > 0L) {
      this.timer = new Timer();
      this.timer.scheduleAtFixedRate(new TimeOutTask(), 0L, this.taskPeriod);
    }
  }

  public void reput(K k) {
    synchronized (this.keyList) {
      if (!this.keyList.contains(k)) return;
      this.keyList.remove(k);
      this.keyList.add(k);
      this.enterTimeMap.put(k, Long.valueOf(System.currentTimeMillis()));
    }
  }

  public void clear()
  {
    synchronized (this.keyList) {
      this.keyList.clear();
      this.enterTimeMap.clear();
      this.map.clear();
    }
  }

  public boolean containsKey(Object key)
  {
    return this.map.containsKey(key);
  }

  public boolean containsValue(Object value)
  {
    return this.map.containsValue(value);
  }

  public Set<Map.Entry<K, V>> entrySet()
  {
    return this.map.entrySet();
  }

  public V get(Object key)
  {
    return this.map.get(key);
  }

  public boolean isEmpty()
  {
    return this.map.isEmpty();
  }

  public Set<K> keySet()
  {
    return this.map.keySet();
  }

  public V put(K key, V value)
  {
    synchronized (this.keyList) {
      if (this.keyList.contains(key)) {
        this.keyList.remove(key);
      }
      this.keyList.add(key);
      this.enterTimeMap.put(key, Long.valueOf(System.currentTimeMillis()));
      return this.map.put(key, value);
    }
  }

  public void putAll(Map<? extends K, ? extends V> m)
  {
    for (Iterator<? extends K> it = m.keySet().iterator(); it.hasNext(); ) {
      K k = it.next();
      put(k, m.get(k));
    }
  }

  public V remove(Object key)
  {
    synchronized (this.keyList) {
      this.keyList.remove(key);
      this.enterTimeMap.remove(key);
      return this.map.remove(key);
    }
  }

  public int size()
  {
    return this.map.size();
  }

  public Collection<V> values()
  {
    return this.map.values();
  }
  private void timeOutRemove(K k) {
    Object v = this.map.get(k);
    if (v == null) return;
    remove(k);
    if ((v instanceof TimeOutAware))
      ((TimeOutAware)v).onTimeOut();
  }

  public static void main(String[] args)
  {
    TimeOutMap tom = new TimeOutMap(10000L);
    tom.put("1", new TimeOutAware()
    {
      public void onTimeOut() {
        System.out.println("1号对象被踢出:" + new Date());
      }
    });
    System.out.println("1号对象被加入：" + new Date());
    try {
      Thread.sleep(5000L);
    }
    catch (InterruptedException e) {
      e.printStackTrace();
    }
    tom.put("2", new TimeOutAware()
    {
      public void onTimeOut() {
        System.out.println("2号对象被踢出:" + new Date());
      }
    });
    System.out.println("2号对象被加入：" + new Date());
  }

  class TimeOutTask extends TimerTask {
    TimeOutTask() {
    }

    public void run() {
      long timeOutTime = System.currentTimeMillis() - TimeOutMap.this.timeOut;
      Object firstKey = null;
      synchronized (TimeOutMap.this.keyList) {
        if (TimeOutMap.this.keyList.size() == 0) return;
        firstKey = TimeOutMap.this.keyList.get(0);
        long firstTime = ((Long)TimeOutMap.this.enterTimeMap.get(firstKey)).longValue();
        if (firstTime > timeOutTime)
        {
          return;
        }
      }
      Object[] keys = TimeOutMap.this.keyList.toArray();

      for (int i = 0; i < keys.length; i++) {
        Long enterTime = (Long)TimeOutMap.this.enterTimeMap.get(keys[i]);
        if (enterTime != null)
          if (enterTime.longValue() < timeOutTime)
            TimeOutMap.this.timeOutRemove((K)keys[i]);
          else
            return;
      }
    }
  }
}